// selection_array datapump implementation
// Copyright 2006 (C) Ralph Thomas

#include <datapumps/selection_array.h>
#include <binder.hpp>
#include <boost/bind.hpp>
#include <toolkit/selection.h>

namespace datapumps {
	class selection_array_datapump_t : public adobe::binder_t {
		toolkit::selection_ptr_t					selection_m;
		toolkit::selection_t::scoped_connection_t	connection_m;
		adobe::value_t								last_m;
		adobe::binder_helper_t						datapump_m;
		adobe::sheet_t::monitor_value_t				value_monitor_m;
		adobe::array_t								params_m;
		adobe::sheet_t&								sheet_m;

		void update_array( const adobe::value_t& a ) {
			if (a == last_m) return;
			last_m = a;
			if (value_monitor_m) value_monitor_m( a );
		}

		void update_selection( const adobe::value_t& v ) {
			if (connection_m.connected())
				connection_m.disconnect();
			if (v.type() != typeid(toolkit::selection_ptr_t)) {
				selection_m = toolkit::selection_ptr_t();
				if (value_monitor_m) value_monitor_m(adobe::value_t(adobe::array_t()));
				return;
			}
			selection_m = v.get<toolkit::selection_ptr_t>();
			selection_m->monitor_all(boost::bind(&selection_array_datapump_t::update_array, this, _1));
		}
	  public:
		selection_array_datapump_t(const adobe::array_t& params, adobe::sheet_t& sheet)
		 : datapump_m(sheet), params_m(params), sheet_m(sheet)
		{
			datapump_m.initialize(params.at(0), boost::bind(&selection_array_datapump_t::update_selection, this, _1), 0);
		}
		virtual ~selection_array_datapump_t() {}
		void set(const adobe::value_t&) {}
		void set_signals(const adobe::sheet_t::monitor_value_t& mv, const adobe::sheet_t::monitor_active_t& ma) {
			if (ma) ma(false);
			value_monitor_m = mv;
			if (mv) mv(adobe::value_t(last_m));
		}
		void substitute(const adobe::name_t& name, const adobe::value_t& value) {
			datapump_m.substitute(name, value);
		}
		void invoke() {}
		adobe::binder_ptr_t copy() const {
			return adobe::binder_ptr_t(new selection_array_datapump_t(params_m, sheet_m));
		}
	};
	adobe::value_t make_selection_array_datapump(const adobe::array_t& params, adobe::sheet_t& sheet) {
		return adobe::value_t(adobe::binder_ptr_t(new selection_array_datapump_t(params, sheet)));
	}
	class selection_navigate_datapump_t : public adobe::binder_t {
		toolkit::selection_ptr_t					selection_m;
		toolkit::selection_t::scoped_connection_t	connection_m;
		adobe::value_t								last_m;
		adobe::binder_helper_t						datapump_m;
		adobe::sheet_t::monitor_value_t				value_monitor_m;
		adobe::sheet_t::monitor_active_t			active_monitor_m;
		adobe::array_t								params_m;
		adobe::sheet_t&								sheet_m;
		bool										next_m;
		
		void update_value( const adobe::value_t& a ) {
			if (a == last_m) return;
			last_m = a;
			if (value_monitor_m) value_monitor_m( a );
			if (active_monitor_m) active_monitor_m( a != adobe::value_t() );
		}

		void update_selection( const adobe::value_t& v ) {
			if (connection_m.connected())
				connection_m.disconnect();
			if (v.type() != typeid(toolkit::selection_ptr_t)) {
				selection_m = toolkit::selection_ptr_t();
				if (value_monitor_m) value_monitor_m(adobe::value_t());
				return;
			}
			selection_m = v.get<toolkit::selection_ptr_t>();
			if (next_m)
				selection_m->monitor_next(boost::bind(&selection_navigate_datapump_t::update_value, this, _1));
			else
				selection_m->monitor_prev(boost::bind(&selection_navigate_datapump_t::update_value, this, _1));
		}
	  public:
		selection_navigate_datapump_t(const adobe::array_t& params, adobe::sheet_t& sheet, bool next)
		 : datapump_m(sheet), params_m(params), sheet_m(sheet), next_m(next)
		{
			datapump_m.initialize(params.at(0), boost::bind(&selection_navigate_datapump_t::update_selection, this, _1), 0);
		}
		virtual ~selection_navigate_datapump_t() {}
		void set(const adobe::value_t&) {}
		void set_signals(const adobe::sheet_t::monitor_value_t& mv, const adobe::sheet_t::monitor_active_t& ma) {
			active_monitor_m = ma;
			value_monitor_m = mv;
			if (mv) mv(last_m);
			if (ma) ma(last_m != adobe::value_t());
		}
		void substitute(const adobe::name_t& name, const adobe::value_t& value) {
			datapump_m.substitute(name, value);
		}
		void invoke() {}
		adobe::binder_ptr_t copy() const {
			return adobe::binder_ptr_t(new selection_navigate_datapump_t(params_m, sheet_m, next_m));
		}
	};
	
	adobe::value_t make_selection_next_datapump(const adobe::array_t& params, adobe::sheet_t& sheet) {
		return adobe::value_t(adobe::binder_ptr_t(new selection_navigate_datapump_t(params, sheet, true)));
	}
	adobe::value_t make_selection_prev_datapump(const adobe::array_t& params, adobe::sheet_t& sheet) {
		return adobe::value_t(adobe::binder_ptr_t(new selection_navigate_datapump_t(params, sheet, false)));
	}

	class selection_primary_datapump_t : public adobe::binder_t {
		toolkit::selection_ptr_t					selection_m;
		toolkit::selection_t::scoped_connection_t	connection_m;
		adobe::value_t								last_m;
		adobe::binder_helper_t						datapump_m;
		adobe::sheet_t::monitor_value_t				value_monitor_m;
		adobe::array_t								params_m;
		adobe::sheet_t&								sheet_m;
		
		void update_value( const adobe::value_t& a ) {
			if (a == last_m) return;
			last_m = a;
			if (value_monitor_m) value_monitor_m( a );
		}

		void update_selection( const adobe::value_t& v ) {
			if (connection_m.connected())
				connection_m.disconnect();
			if (v.type() != typeid(toolkit::selection_ptr_t)) {
				selection_m = toolkit::selection_ptr_t();
				if (value_monitor_m) value_monitor_m(adobe::value_t());
				return;
			}
			selection_m = v.get<toolkit::selection_ptr_t>();
			selection_m->monitor_primary(boost::bind(&selection_primary_datapump_t::update_value, this, _1));
		}
	  public:
		selection_primary_datapump_t(const adobe::array_t& params, adobe::sheet_t& sheet)
		 : datapump_m(sheet), params_m(params), sheet_m(sheet)
		{
			datapump_m.initialize(params.at(0), boost::bind(&selection_primary_datapump_t::update_selection, this, _1), 0);
		}
		virtual ~selection_primary_datapump_t() {}
		void set(const adobe::value_t& v) {
            //
            // This is an index into the selection.
            //
			if (selection_m) selection_m->set_primary(v);
		}
		void set_signals(const adobe::sheet_t::monitor_value_t& mv, const adobe::sheet_t::monitor_active_t& ma) {
			if (ma) ma(true);
			value_monitor_m = mv;
			if (mv) mv(adobe::value_t(last_m));
		}
		void substitute(const adobe::name_t& name, const adobe::value_t& value) {
			datapump_m.substitute(name, value);
		}
		void invoke() {}
		adobe::binder_ptr_t copy() const {
			return adobe::binder_ptr_t(new selection_primary_datapump_t(params_m, sheet_m));
		}
	};
	adobe::value_t make_selection_primary_datapump(const adobe::array_t& params, adobe::sheet_t& sheet) {
		return adobe::value_t(adobe::binder_ptr_t(new selection_primary_datapump_t(params, sheet)));
	}
}
